package com.easylinkin.linkappapi.shigongyun.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeNodeConfig;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.easylinkin.linkappapi.buildcheckinfo.dao.BuildCheckInfoMapper;
import com.easylinkin.linkappapi.config.entity.Config;
import com.easylinkin.linkappapi.dashboard.entity.Dashboard;
import com.easylinkin.linkappapi.dashboard.mapper.DashboardMapper;
import com.easylinkin.linkappapi.device.entity.Device;
import com.easylinkin.linkappapi.device.mapper.DeviceMapper;
import com.easylinkin.linkappapi.deviceattributestatus.entity.DeviceAttributeStatus;
import com.easylinkin.linkappapi.deviceattributestatus.mapper.DeviceAttributeStatusMapper;
import com.easylinkin.linkappapi.deviceattributestatus.service.DeviceAttributeStatusService;
import com.easylinkin.linkappapi.devicetype.mapper.DeviceTypeMapper;
import com.easylinkin.linkappapi.elasticsearch.service.impl.EsServiceImpl;
import com.easylinkin.linkappapi.iaqi.config.IaqiConfig;
import com.easylinkin.linkappapi.iaqi.config.IaqiConfigUtil;
import com.easylinkin.linkappapi.lobar.entity.UserProject;
import com.easylinkin.linkappapi.lobar.mapper.UserClockMapper;
import com.easylinkin.linkappapi.lobar.mapper.UserGateMapper;
import com.easylinkin.linkappapi.lobar.mapper.UserProjectMapper;
import com.easylinkin.linkappapi.lobar.mapper.UserRecordMapper;
import com.easylinkin.linkappapi.lobar.service.LobarScreenService;
import com.easylinkin.linkappapi.lobar.util.ProjectTenatUtils;
import com.easylinkin.linkappapi.manageinfo.dao.ManageInfoMapper;
import com.easylinkin.linkappapi.manageinfo.entity.ManageInfo;
import com.easylinkin.linkappapi.project.entity.dto.ProjectFromEnterprise;
import com.easylinkin.linkappapi.project.service.ProjectService;
import com.easylinkin.linkappapi.quality.dto.QualityQuestionInfoDto;
import com.easylinkin.linkappapi.quality.mapper.QualityQuestionInfoMapper;
import com.easylinkin.linkappapi.safe.dao.HiddenDangerMapper;
import com.easylinkin.linkappapi.safe.dto.HiddenDangerCountDTO;
import com.easylinkin.linkappapi.safe.service.HiddenDangerService;
import com.easylinkin.linkappapi.shigongyun.dto.UserGateClockDTO;
import com.easylinkin.linkappapi.shigongyun.service.EnterpriseEditionBiService;
import com.easylinkin.linkappapi.shigongyun.vo.DeviceCountVo;
import com.easylinkin.linkappapi.shigongyun.vo.LaborAttendanceVo;
import com.easylinkin.linkappapi.shigongyun.vo.TenantProjectCountVo;
import com.easylinkin.linkappapi.shigongyun.vo.UserClockGateVo;
import com.easylinkin.linkappapi.tenant.dto.LinkappTenantDTO;
import com.easylinkin.linkappapi.tenant.entity.LinkappTenant;
import com.easylinkin.linkappapi.tenant.sevice.LinkappTenantService;
import com.easylinkin.linkappapi.tenantproject.dao.TenantProjectMapper;
import com.easylinkin.linkappapi.tenantproject.dto.TenantProjectDto;
import com.easylinkin.linkappapi.tenantproject.entity.TenantProject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import site.morn.rest.RestBuilders;
import site.morn.rest.RestMessage;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * @author zhengwen
 */
@Slf4j
@Service
public class EnterpriseEditionBiServiceImpl implements EnterpriseEditionBiService {

    private final static String baseKey = "enterprise:bigScreen:";

    private final static String projectOutputConfigKey = "PROJECT_OUTPUT_TARGET";

    @Resource
    private UserRecordMapper userRecordMapper;

    @Resource
    private DeviceTypeMapper deviceTypeMapper;

    @Resource
    private UserClockMapper userClockMapper;

    @Resource
    private UserGateMapper userGateMapper;

    @Resource
    private TenantProjectMapper tenantProjectMapper;

    @Resource
    private BuildCheckInfoMapper buildCheckInfoMapper;

    @Resource
    private DeviceMapper deviceMapper;

    @Resource
    private EsServiceImpl esService;

    @Resource
    private IaqiConfigUtil iaqiConfigUtil;

    @Resource
    private ManageInfoMapper manageInfoMapper;

    @Resource
    private DeviceAttributeStatusMapper attributeStatusMapper;

    @Resource
    private UserProjectMapper userProjectMapper;

    @Resource
    private DashboardMapper dashboardMapper;

    @Resource
    private LinkappTenantService linkappTenantService;

    @Resource
    private DeviceAttributeStatusService deviceAttributeStatusService;

    @Resource
    private LobarScreenService lobarScreenService;

    @Autowired
    private QualityQuestionInfoMapper questionInfoMapper;

    @Autowired
    private ProjectService projectService;

    @Resource
    private HiddenDangerService hiddenDangerService;

    @Resource
    private HiddenDangerMapper hiddenDangerMapper;


    private final String pmConfigKey = "IAQI_STATISTICS_DEVICE_CODES";

    @Override
    public RestMessage laborAttendanceData(LaborAttendanceVo laborAttendanceVo) {
        //app_user_record 记录
        //处理时间
        Date now = DateUtil.date();
        Date startTime = laborAttendanceVo.getStartTime();
        Date endTime = laborAttendanceVo.getEndTime();
        if (startTime == null && endTime == null) {
            endTime = DateUtil.endOfMonth(DateUtil.offsetMonth(now, -1));
            startTime = DateUtil.beginOfMonth(DateUtil.offsetMonth(endTime, -5));
        } else {
            endTime = DateUtil.endOfMonth(endTime);
            startTime = DateUtil.beginOfMonth(startTime);
        }

        //本来想单表查询，但是怕后面跟项目关联，为保证健壮性，还是辛苦下，自己sql
        //今日在场总人数
        /*laborAttendanceVo.setStartTime(DateUtil.beginOfDay(now));
        laborAttendanceVo.setEndTime(DateUtil.endOfDay(now));
        Integer dayAtNum = userRecordMapper.countNumByParam(laborAttendanceVo);*/
        //20220810产品确定，在场统计：select * from app_user_project aup where status_ =1;
        UserProject userProject = new UserProject();
        userProject.setStatus(1);
        Integer dayAtNum = userProjectMapper.countUserNumBy(userProject);

        //本月出勤总人数
        laborAttendanceVo.setStartTime(DateUtil.beginOfMonth(now));
        laborAttendanceVo.setEndTime(DateUtil.endOfMonth(now));
        Integer monthAtNum = userRecordMapper.countNumByParam(laborAttendanceVo);

        //时间段统计数据（按月）
        laborAttendanceVo.setStartTime(startTime);
        laborAttendanceVo.setEndTime(endTime);
        List<Map<String, Object>> monthGroupAtList = userRecordMapper.countGroupByMonth(laborAttendanceVo);

        //数据补全
        TreeMap<String,Object> treeMap = compLaborAttendanceData(monthGroupAtList, laborAttendanceVo.getStartTime(), laborAttendanceVo.getEndTime());
        /*List<Map<String,String>>  ls = new ArrayList<>();
        if(CollectionUtil.isNotEmpty(treeMap)){

            for (Map.Entry e: treeMap.entrySet()) {
                String key = e.getKey().toString();
                String val = e.getValue().toString();
                Map<String,String> mp = new HashMap<>();
                mp.put(key, val);
                ls.add(mp);
            }
        }*/

        //组织出参
        /*JSONObject json = JSONUtil.createObj();
        json.putOpt("dayAtNum", dayAtNum);
        json.putOpt("monthAtNum", monthAtNum);
        json.putOpt("monthGroupData", treeMap);
        json.putOpt("monthGroupData", ls);*/
        //hutool的json有bug，里面放treemap顺序乱了
        com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
        json.put("dayAtNum", dayAtNum);
        json.put("monthAtNum", monthAtNum);
        //json.put("monthGroupDatatree", treeMap);
        json.put("monthGroupData", treeMap);

        return RestBuilders.successBuilder().data(json).build();
    }

    @Override
    public RestMessage deviceTypeNumData(DeviceCountVo deviceCountVo) {
        List<Map<String, Object>> groupByTypeList = deviceTypeMapper.countGroupByDeviceTypeGlobal(deviceCountVo);

        return RestBuilders.successBuilder().data(groupByTypeList).build();
    }

    @Override
    public RestMessage userClockGateData(UserClockGateVo userClockGateVo) {
        Date startTime = userClockGateVo.getStartTime();
        Date endTime = userClockGateVo.getEndTime();
        if (startTime == null && endTime == null) {
            Date now = DateUtil.date();
            startTime = now;
            endTime = now;
        }
        userClockGateVo.setStartTime(DateUtil.beginOfDay(startTime));
        userClockGateVo.setEndTime(DateUtil.endOfDay(endTime));
        //进出数量统计
        List<Map<String, Object>> inOutCountLs = userClockMapper.countNumGroupByInOut(userClockGateVo);
        //黄码、红码数量统计
        List<Map<String, Object>> healthCountLs = userGateMapper.countNumGroupByHealthCode(userClockGateVo);

        //进出最新记录
        List<UserGateClockDTO> userGateClockDTOList = userGateMapper.queryGateClockDtoList(userClockGateVo);

        //组织出参
        JSONObject json = new JSONObject();
        json.putOpt("inOutCount", inOutCountLs);
        json.putOpt("healthCount", healthCountLs);
        json.putOpt("healthPassRecord", userGateClockDTOList);
        return RestBuilders.successBuilder().data(json).build();
    }

    @Override
    public RestMessage projectCountAndClassData(TenantProjectCountVo tenantProjectCountVo) {
        //时间
        Date now = DateUtil.date();
        Date startTime = DateUtil.beginOfYear(now);
        Date endTime = DateUtil.endOfYear(now);
        tenantProjectCountVo.setStartTime(startTime);
        tenantProjectCountVo.setEndTime(endTime);

        //项目优创分组统计数量
        List<Map<String, Object>> countClassLs = tenantProjectMapper.countNumGroupByClass(tenantProjectCountVo);

        //查询去年项目总数
        QueryWrapper lastYearQueryWrapper = new QueryWrapper();
        lastYearQueryWrapper.eq("delete_state", 1);
        lastYearQueryWrapper.ge("fill_dt", DateUtil.offset(startTime, DateField.YEAR, -1));
        lastYearQueryWrapper.le("fill_dt", DateUtil.offset(endTime, DateField.YEAR, -1));

        int lastYearNum = tenantProjectMapper.selectCount(lastYearQueryWrapper);

        //查询今年项目总数
        QueryWrapper curYearQueryWrapper = new QueryWrapper();
        curYearQueryWrapper.eq("delete_state", 1);
        curYearQueryWrapper.ge("fill_dt", startTime);
        curYearQueryWrapper.le("fill_dt", endTime);
        int curYearNum = tenantProjectMapper.selectCount(curYearQueryWrapper);

        //环比值
        double chainRatio = 0d;
        if (lastYearNum != 0) {
            chainRatio = NumberUtil.div(NumberUtil.sub(curYearNum, lastYearNum), lastYearNum, 2);
        }

        //组织出参
        JSONObject json = new JSONObject();
        json.putOpt("curYearNum", curYearNum);
        json.putOpt("chainRatio", NumberUtil.round(chainRatio, 2));
        json.putOpt("countClassLs", countClassLs);

        return RestBuilders.successBuilder().data(json).build();
    }

    @Override
    public RestMessage projectTypeCountData(TenantProjectCountVo tenantProjectCountVo) {
        //时间
        Date now = DateUtil.date();
        Date startTime = DateUtil.beginOfYear(now);
        Date endTime = DateUtil.endOfYear(now);
        tenantProjectCountVo.setStartTime(startTime);
        tenantProjectCountVo.setEndTime(endTime);

        //项目类型分组统计
        List<Map<String, Object>> countTypeLs = tenantProjectMapper.countNumGroupByType(tenantProjectCountVo);

        return RestBuilders.successBuilder().data(countTypeLs).build();
    }

    @Override
    public RestMessage outputEndData(TenantProjectCountVo tenantProjectCountVo) {
        /* 20220809产品反馈固定，不从新中大同步，这里设计做到配置
        //时间
        Date now = DateUtil.date();
        Date startTime = DateUtil.beginOfYear(now);
        Date endTime = DateUtil.endOfYear(now);
        tenantProjectCountVo.setStartTime(startTime);
        tenantProjectCountVo.setEndTime(endTime);

        String outputTarget = tenantProjectCountVo.getOutputTarge();

        //从配置查询年度产值目标
        String outputKey = baseKey + "output:" + DateUtil.format(now, "yyyy") + ":target";
        Object outputTargetObj = redisUtil.get("outputKey");
        if (outputTargetObj != null) {
            outputTarget = String.valueOf(outputTargetObj);
        }

        if (StringUtils.isBlank(outputTarget) || !NumberUtil.isNumber(outputTarget)) {
            outputTarget = "0";
        }

        //统计当前年完成产值
        BigDecimal finishOutput = tenantProjectMapper.sumProjectOutput(tenantProjectCountVo);

        //组织出参
        double outputTargetDouble = NumberUtil.div(new BigDecimal(outputTarget), 100000000, 2).doubleValue();
        double finishOutputDouble = NumberUtil.div(finishOutput, 100000000, 2).doubleValue();
        JSONObject json = new JSONObject();
        json.putOpt("targetOutput", NumberUtil.round(outputTargetDouble, 2) + "亿");
        json.putOpt("finishOutput", NumberUtil.round(finishOutputDouble, 2) + "亿");
        if (NumberUtil.compare(outputTargetDouble, 0d) != 0) {
            json.putOpt("endRate", NumberUtil.round(NumberUtil.div(finishOutputDouble, outputTargetDouble, 2), 2));
        } else {
            json.putOpt("endRate", 0);
        }*/
        JSONObject json = new JSONObject();
        String tenantId = tenantProjectCountVo.getTenantId();
        Config config = iaqiConfigUtil.getOneConfigs(projectOutputConfigKey, tenantId);
        if (config != null) {
            String configVal = config.getValue();
            JSONArray jsonArray = JSONUtil.parseArray(configVal);
            if (CollectionUtil.isNotEmpty(jsonArray)) {
                json = (JSONObject) jsonArray.get(0);
                String targetOutput = json.getStr("targetOutput");
                String finishOutput = json.getStr("finishOutput");
                if (StringUtils.isNotBlank(targetOutput) && StringUtils.isNotBlank(finishOutput)) {
                    Double outputTargetDouble = Double.parseDouble(targetOutput.trim().replace("亿", ""));
                    Double finishOutputDouble = Double.parseDouble(finishOutput.trim().replace("亿", ""));
                    json.putOpt("endRate", NumberUtil.round(NumberUtil.div(finishOutputDouble, outputTargetDouble, 2), 2));
                }
            }
        }

        return RestBuilders.successBuilder().data(json).build();
    }

    @Override
    public RestMessage projectStatueCountData(TenantProjectCountVo tenantProjectCountVo) {
        //时间
        Date now = DateUtil.date();
        Date startTime = DateUtil.beginOfYear(now);
        Date endTime = DateUtil.endOfYear(now);
        tenantProjectCountVo.setStartTime(startTime);
        tenantProjectCountVo.setEndTime(endTime);

        List<Map<String, Object>> countTypeLs = tenantProjectMapper.countNumGroupByStatue(tenantProjectCountVo);

        return RestBuilders.successBuilder().data(countTypeLs).build();
    }

    @Override
    public RestMessage projectInfoData(TenantProjectCountVo tenantProjectCountVo) {
        String id = tenantProjectCountVo.getId();
        String tenantId = tenantProjectCountVo.getTenantId();
        JSONObject json = new JSONObject();

        //查询项目信息
        TenantProjectDto tenantProjectDto = tenantProjectMapper.selectOneTenantProjectDto(tenantProjectCountVo);
        if (tenantProjectDto != null) {
            //20220809产品这时候才确定，项目进度公式：(当前时间-计划开工时间）/（计划竣工时间-计划开工时间)
            //sb，这样写的还是不严谨嘛，当前时间早于计划开工时间，应该直接就是0，当前时间晚于计划开工时间应该就设置成100
            Date now = DateUtil.beginOfDay(DateUtil.date());
            Date startTime = DateUtil.beginOfDay(tenantProjectDto.getStartDate());
            Date endTime = DateUtil.endOfDay(tenantProjectDto.getEndDate());
            if (now.before(startTime)) {
                tenantProjectDto.setProgress("0");
            } else {
                long sbd = DateUtil.betweenDay(startTime, now, true);
                long ebd = DateUtil.betweenDay(startTime, endTime, true);
                double progress = NumberUtil.div(sbd, ebd, 2);
                tenantProjectDto.setProgress(String.valueOf(progress));
            }
            tenantId = tenantProjectDto.getTenantId();
            if (StringUtils.isNotBlank(tenantId)) {
                QueryWrapper<Dashboard> queryWrapper = new QueryWrapper<>();
                queryWrapper.eq("tenant_id", tenantId);
                Dashboard dashboard = dashboardMapper.selectOne(queryWrapper);
                tenantProjectDto.setDashboard(dashboard);
                tenantProjectCountVo.setTenantId(tenantId);
            }
        }

        //暂定一个设备查询环境信息
        if (StringUtils.isNotBlank(tenantId)) {
            Map<String, Object> environmentData = getTenantEnvironmentData(tenantProjectCountVo);
            json.putOpt("environmentData", environmentData);
        }

        //统计设备在离线
        if (StringUtils.isNotBlank(tenantId)) {
            Device device = new Device();
            device.setTenantId(tenantId);
            List<Map<String, Object>> deviceOnLineLs = deviceMapper.countDeviceByOnlineStateGlobal(device);
            json.putOpt("deviceOnLineData", deviceOnLineLs);

            //闸机数据
            //实时人数：今日进打卡数 > 出打卡数，今日考勤：有进打卡
            tenantProjectCountVo.setStartTime(DateUtil.beginOfDay(DateUtil.date()));
            tenantProjectCountVo.setEndTime(DateUtil.endOfDay(tenantProjectCountVo.getStartTime()));
            List<Map<String, Object>> userClockMpLs = userClockMapper.countNumGroupByUserAndDire(tenantProjectCountVo);
            Map<String, Integer> peopleClockMp = transCalPeopleNum(userClockMpLs);
            json.putOpt("peopleData", peopleClockMp);
        }


        //安全检查数据,根基checkType分组
        if (!StringUtils.isAnyBlank(id, tenantId)) {
            List<Map<String, Object>> checkTypeData = buildCheckInfoMapper.countGroupByTypeMonth(tenantProjectCountVo);
            json.putOpt("checkTypeData", checkTypeData);
        }

        //组织出参
        json.putOpt("tenantProject", tenantProjectDto);

        return RestBuilders.successBuilder().data(json).build();
    }


    @Override
    public RestMessage projectInfoData2(TenantProjectCountVo tenantProjectCountVo) {
        //企业级项目id
        String projectId = tenantProjectCountVo.getId();
        //获取tenantId
        QueryWrapper<LinkappTenant> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("project_id",projectId);
        LinkappTenant one = linkappTenantService.getOne(queryWrapper);
        if(one==null){
            throw new RuntimeException("项目id找不到对应的应用");
        }
        TenantProjectDto tenantProjectDto = new TenantProjectDto();
        tenantProjectDto.setLinkappTenant(one);
        tenantProjectDto.setPlatformProjectName(one.getPlatformProjectName());
        tenantProjectDto.setConstructionUnit(one.getShigongUnit());//承建单位->施工单位
        tenantProjectDto.setBiddingUnit(one.getConstructionUnit());//建设单位
        tenantProjectDto.setLocation(one.getLocation());//项目地点
        tenantProjectDto.setBuildVol(one.getArea()==null?"--":one.getArea().toString());//建筑体量->建筑面积
        tenantProjectDto.setContractAmount(one.getProjectAmount()==null?new BigDecimal(0):new BigDecimal(one.getProjectAmount()));//合同数->项目金额
        //处理当前环境数据deviceAttributeStatus/getDeviceRealtimeDataByCertainTag?deviceTypeName=扬尘监测系统
        String tenantId = one.getId();
        DeviceAttributeStatus status1 = new DeviceAttributeStatus();
        status1.setTenantId(tenantId);
        List<DeviceAttributeStatus> status = deviceAttributeStatusService
            .getDeviceRealtimeDataByCertainTag(status1, "扬尘监测系统");
        JSONObject json = new JSONObject();
        json.putOpt("environmentData", status);
        //处理设备数跟在线数
        //统计设备在离线
        if (StringUtils.isNotBlank(tenantId)) {
            //填充大屏信息
            QueryWrapper<Dashboard> queryWrapper1 = new QueryWrapper<>();
            queryWrapper1.eq("tenant_id", tenantId);
            Dashboard dashboard = dashboardMapper.selectOne(queryWrapper1);
            tenantProjectDto.setDashboard(dashboard);

            Device device = new Device();
            device.setTenantId(tenantId);
            List<Map<String, Object>> deviceOnLineLs = deviceMapper.countDeviceByOnlineStateGlobal(device);
            json.putOpt("deviceOnLineData", deviceOnLineLs);

            //闸机数据
            //实时人数：今日进打卡数 > 出打卡数，今日考勤：有进打卡
            Map<String, Integer> stringIntegerMap = lobarScreenService.onNum(null, tenantId);
            json.putOpt("peopleData", stringIntegerMap);

            //安全跟质量数据
            //质量统计qualityInspectionAccount/count
            QualityQuestionInfoDto qualityQuestionInfoDto = new QualityQuestionInfoDto();
            qualityQuestionInfoDto.setTenantId(tenantId);
            Integer i = questionInfoMapper.getCount(qualityQuestionInfoDto);
            Map<String,Object> map = new HashMap<>();
            map.put("total",i);
            json.putOpt("quality",map);
            //安全统计hiddenDanger/getCount
            HiddenDangerCountDTO hiddenDanger = hiddenDangerService
                .getCount(null, null, null, null, null, tenantId);
            json.putOpt("hiddenDanger",hiddenDanger);
        }

        //计算进度
        LinkappTenantDTO linkappTenantDTO = new LinkappTenantDTO();
        BeanUtil.copyProperties(one,linkappTenantDTO);
        ProjectTenatUtils.calculateProgress(linkappTenantDTO);

        //组织出参
        json.putOpt("linkappTenantDTO", linkappTenantDTO);
        tenantProjectDto.setProgress(linkappTenantDTO.getProgress());
        json.putOpt("tenantProjectDto", tenantProjectDto);

        //获取项目图片
        ProjectFromEnterprise info = projectService.getInfo(tenantId);
        json.putOpt("info", info);
        return RestBuilders.successBuilder().data(json).build();
    }

    private Map<String, Integer> transCalPeopleNum(List<Map<String, Object>> userClockMpLs) {
        Map<String, Integer> mp = new HashMap<>();
        AtomicReference<Integer> realPeopleNum = new AtomicReference<>(0);
        AtomicReference<Integer> workPeopleNum = new AtomicReference<>(0);
        if (CollectionUtil.isNotEmpty(userClockMpLs)) {
            //转map
            Map<String, Object> userDireMap = new HashMap<>();
            userClockMpLs.stream().forEach(c -> {
                StringBuilder keySb = new StringBuilder();
                String val = null;
                Iterator<Map.Entry<String, Object>> it = c.entrySet().iterator();
                while (it.hasNext()) {
                    Map.Entry<String, Object> entry = it.next();
                    String tmpKey = entry.getKey();
                    Object tmpValObj = entry.getValue();
                    if ("userId".equals(tmpKey)) {
                        keySb.append(tmpValObj.toString()).append(",");
                    }
                    if ("direction".equals(tmpKey)) {
                        keySb.append(tmpValObj.toString());
                    }
                    if ("num".equals(tmpKey)) {
                        val = String.valueOf(tmpValObj);
                    }
                }
                userDireMap.put(keySb.toString(), c);
            });

            Map<String, String> workUserMp = new HashMap();
            //计算 1进门2出门
            userClockMpLs.stream().forEach(c -> {
                String userId = c.get("userId").toString();
                workUserMp.put(userId, userId);
                String inKey = userId + "," + 1;
                String outKey = userId + "," + 2;
                Object inObj = userDireMap.get(inKey);
                Object outObj = userDireMap.get(outKey);
                //实际在场数量
                if (inObj != null) {
                    if (outObj == null) {
                        //实际在场
                        realPeopleNum.getAndSet(realPeopleNum.get() + 1);
                    } else {
                        JSONObject inJson = JSONUtil.parseObj(inObj);
                        JSONObject outJson = JSONUtil.parseObj(outObj);
                        Integer inNum = inJson.getInt("num");
                        Integer outNum = outJson.getInt("num");
                        Date inTime = inJson.getDate("clockTime");
                        Date outTime = outJson.getDate("clockTime");
                        if (inTime.after(outTime)) {
                            //进的时间，晚于出的时间，认为还在场
                            realPeopleNum.getAndSet(realPeopleNum.get() + 1);
                        }
                       /* if (inNum > outNum ){
                            //进次数 > 出次数，表示实时在场
                            realPeopleNum.getAndSet(realPeopleNum.get() + 1);
                            if (inTime.after(outTime)){
                                realPeopleNum.getAndSet(realPeopleNum.get() + 1);
                            }
                        }*/
                    }
                }
                userDireMap.remove(inKey);
                userDireMap.remove(outObj);
            });
            //出勤数量
            workPeopleNum.set(workUserMp.size());
        }
        mp.put("realPeopleNum", realPeopleNum.get());
        mp.put("workPeopleNum", workPeopleNum.get());
        return mp;
    }

    /**
     * 获取环境数据
     *
     * @param tenantProjectCountVo 租户想统计vo
     * @return 环境数据map
     */
    private Map getTenantEnvironmentData(TenantProjectCountVo tenantProjectCountVo) {
        String tenantId = tenantProjectCountVo.getTenantId();
        List<IaqiConfig> configs = iaqiConfigUtil.getConfigs(pmConfigKey, tenantId);
        String deviceCode = "";
        String pm10key = null;
        String pm25key = null;
        for (IaqiConfig config : configs) {
            List<String> deviceCodes = config.getDeviceCodes();
            if (CollectionUtil.isNotEmpty(deviceCodes)) {
                deviceCode = deviceCodes.get(0);
            }
            List<String> attrs = config.getAttrs();
            pm10key = attrs.get(0);
            pm25key = attrs.get(1);
        }
        Device device = new Device();
        device.setTenantId(tenantProjectCountVo.getTenantId());
        device.setCode(deviceCode);
        Map<String, Object> mp = new HashMap<>();
        List<DeviceAttributeStatus> attrLs = attributeStatusMapper.getDeviceRealtimeDataByDeviceGlobal(device);
        if (CollectionUtil.isNotEmpty(attrLs)) {
            Map<String, String> attrMp = attrLs.stream().collect(Collectors.toMap(DeviceAttributeStatus::getPropCode,DeviceAttributeStatus::getPropValue,(o,n)->n));
            mp.put("pm10", attrMp.get(pm10key));
            mp.put("pm25", attrMp.get(pm25key));
        }
        /* 20220809修改，固定一个扬尘监测设备，不要平均
        Map<String, Object> mp = new HashMap<>();
        Date startTime = tenantProjectCountVo.getStartTime();
        Date endTime = tenantProjectCountVo.getEndTime();
        if (startTime == null && endTime == null) {
            Date now = DateUtil.date();
            endTime = DateUtil.endOfDay(now);
            startTime = DateUtil.beginOfDay(DateUtil.offsetDay(now, -1));
        }
        String tenantId = tenantProjectCountVo.getTenantId();
        List<IaqiConfig> configs = iaqiConfigUtil.getConfigs(pmConfigKey, tenantId);
        for (IaqiConfig config : configs) {
            List<String> deviceCodes = config.getDeviceCodes();
            List<String> attrs = config.getAttrs();
            String pm10key = attrs.get(0);
            String pm25key = attrs.get(1);

            String start = DateUtil.format(startTime, DatePattern.NORM_DATETIME_PATTERN);
            String end = DateUtil.format(endTime, DatePattern.NORM_DATETIME_PATTERN);

            DeviceQueryVo deviceQueryVo = new DeviceQueryVo();
            deviceQueryVo.setQueryTimeEnd(end);
            deviceQueryVo.setQueryTimeStart(start);
            deviceQueryVo.setDeviceCodeList(deviceCodes);
            deviceQueryVo.setTenantId(tenantId);
            IaqiStatistic iaqiStatistic10 = getIaqiStatistic(deviceQueryVo, pm10key);
            IaqiStatistic iaqiStatistic25 = getIaqiStatistic(deviceQueryVo, pm25key);
            int pm10 = iaqiStatistic10 != null && iaqiStatistic10.getStatisticValue() != null ? iaqiStatistic10.getStatisticValue().intValue() : 0;
            int pm25 = iaqiStatistic25 != null && iaqiStatistic25.getStatisticValue() != null ? iaqiStatistic25.getStatisticValue().intValue() : 0;

            mp.put("pm10", pm10);
            mp.put("pm25", pm25);
        }*/
        return mp;
    }

    @Override
    public RestMessage safeProductionData(TenantProjectCountVo tenantProjectCountVo) {
        //时间
        Date startTime = tenantProjectCountVo.getStartTime();
        Date endTime = tenantProjectCountVo.getEndTime();
        if (startTime == null || endTime == null) {
            Date now = DateUtil.date();
            startTime = DateUtil.beginOfYear(now);
            endTime = DateUtil.endOfDay(now);
        }

        tenantProjectCountVo.setStartTime(startTime);
        tenantProjectCountVo.setEndTime(endTime);

        //检查等级
        List<Integer> checkLevelList = tenantProjectCountVo.getCheckLevelList();

        //安全检查统计根据级别类型、月分组
        List<Map<String, Object>> countSafeProductLs = buildCheckInfoMapper.countGroupByLevelMonth(tenantProjectCountVo);

        //数据补全
        Map<Integer, TreeMap> safeProductMpList = compSafeProductionData(countSafeProductLs, startTime, endTime, checkLevelList);

        return RestBuilders.successBuilder().data(safeProductMpList).build();
    }

    @Override
    public RestMessage manageAreaProjectData(TenantProjectCountVo tenantProjectCountVo) {
        List<TreeNode<String>> treeNodeList = new ArrayList<>();

        String rootPid = "-2";
        //查询中国的行政区域
        QueryWrapper<ManageInfo> manageInfoQueryWrapper = new QueryWrapper<>();
        manageInfoQueryWrapper.eq("mange_code", "+86");
        manageInfoQueryWrapper.eq("type", "1");
        ManageInfo countryInfo = manageInfoMapper.selectOne(manageInfoQueryWrapper);
        if (countryInfo != null) {
            String parentIds = ",-1," + countryInfo.getId() + ",";
            QueryWrapper<ManageInfo> miqw = new QueryWrapper<>();
            miqw.like("parent_ids", parentIds + "%");
            List<ManageInfo> pcaList = manageInfoMapper.selectList(miqw);
            if (CollectionUtil.isNotEmpty(pcaList)) {
                //转map
                Map<Long, ManageInfo> pcaMap = pcaList.stream().collect(Collectors.toMap(ManageInfo::getId, c -> c));

                //查询所有项目DtoList
                TenantProject tenantProject = new TenantProject();
                tenantProject.setDeleteState(1);
                List<TenantProjectDto> tenantProjectDtoList = tenantProjectMapper.selectTenantProjectDtoList(tenantProject);

                if (CollectionUtil.isNotEmpty(tenantProjectDtoList)) {
                    //转map
                    Map<Long, TenantProjectDto> tenantProjectDtoMap = tenantProjectDtoList.stream().collect(Collectors.toMap(TenantProjectDto::getId, c -> c));

                    treeNodeList = calAndCoverData(pcaMap, tenantProjectDtoMap, rootPid);
                }
            }
        }

        //构建树数据
        List<Tree<String>> treeList = buildTreeData(treeNodeList, rootPid);

        return RestBuilders.successBuilder().data(treeList).build();
    }

    @Override
    public RestMessage projectHeatMapData(TenantProjectCountVo tenantProjectCountVo) {
        List<Map<String, Object>> heatMapLs = new ArrayList<>();
        String manageId = tenantProjectCountVo.getId();
        if (StringUtils.isNotBlank(manageId)) {
            //查询选择的行政区
            ManageInfo manageInfo = manageInfoMapper.selectById(manageId);
            if (manageInfo != null) {
                int type = manageInfo.getType();
                tenantProjectCountVo.setDataType(String.valueOf(type));

                String parentIds = manageInfo.getParentIds() + manageInfo.getId() + ",";
                tenantProjectCountVo.setId(parentIds);
                //热力图就是区域项目数量统计
                heatMapLs = tenantProjectMapper.countNumGroupByArea(tenantProjectCountVo);
            }
        }

        return RestBuilders.successBuilder().data(heatMapLs).build();
    }

    @Override
    public RestMessage safeProductionData2(TenantProjectCountVo tenantProjectCountVo) {
        //时间
        Date startTime = tenantProjectCountVo.getStartTime();
        Date endTime = tenantProjectCountVo.getEndTime();
        if (startTime == null || endTime == null) {
            Date now = DateUtil.date();
            startTime = DateUtil.beginOfYear(now);
            endTime = DateUtil.endOfDay(now);
        }

        tenantProjectCountVo.setStartTime(startTime);
        tenantProjectCountVo.setEndTime(endTime);

        //检查等级
        List<Integer> checkLevelList = tenantProjectCountVo.getCheckLevelList();
        //安全检查统计根据级别类型、月分组
        List<Map<String, Object>> countSafeProductLs =  hiddenDangerMapper.countGroupByLevelMonth(tenantProjectCountVo);

        //数据补全
        Map<Integer, TreeMap> safeProductMpList = compSafeProductionData(countSafeProductLs, startTime, endTime, checkLevelList);

        return RestBuilders.successBuilder().data(safeProductMpList).build();
    }

    private List<Tree<String>> buildTreeData(List<TreeNode<String>> nodeList, String rootPid) {
        //配置
        TreeNodeConfig treeNodeConfig = new TreeNodeConfig();
        //这个字段排序用，这里用创建时间
        treeNodeConfig.setWeightKey("sortNo");
        treeNodeConfig.setIdKey("id");
        // 最大递归深度
        treeNodeConfig.setDeep(5);
        Map<String, Integer> manageProjectNumMp = new HashMap<>();
        List<Tree<String>> treeList = TreeUtil.build(nodeList, rootPid, treeNodeConfig, (treeNode, tree) -> {

            Map<String, Object> extMp = treeNode.getExtra();
            String levelDataType = "0";

            Object levelDataTypeObj = extMp.get("dataType");
            if (levelDataTypeObj != null) {
                levelDataType = levelDataTypeObj.toString();
            }

            //数据类型
            if ("projectInfo".equals(levelDataType)) {
                tree.put("tenantId", extMp.get("tenantId"));
            }

            tree.setId(treeNode.getId());
            tree.setParentId(treeNode.getParentId());
            tree.setWeight(Long.parseLong(treeNode.getWeight().toString()));
            tree.setName(treeNode.getName());

            // 扩展属性 ...
            Object isClassObj = extMp.get("isClass");
            if (isClassObj != null) {
                tree.putExtra("isClass", isClassObj.toString());
                if ("1".equals(isClassObj.toString())) {
                    Object pcClassObj = extMp.get("pcClass");
                    if (pcClassObj != null) {
                        tree.putExtra("pcClass", pcClassObj.toString());
                    }
                }
            }

            tree.put("dataType", levelDataType);
            tree.put("projectNum", extMp.get("projectNum"));

            tree.putExtra("extraInfo", extMp);

        });
        return treeList;
    }

    private List<TreeNode<String>> calAndCoverData(Map<Long, ManageInfo> pcaMap, Map<Long, TenantProjectDto> tenantProjectDtoMap, String rootPid) {
        List<TreeNode<String>> nodeList = new ArrayList<>();


        //根
        TreeNode root = new TreeNode("-1", rootPid, "区域", -1);
        root.setExtra(JSONUtil.parseObj(root));
        root.getExtra().put("dataType", "rootInfo");

        nodeList.add(root);

        //迭代组织node
        Map<String, Integer> manageProjectNumMp = new HashMap<>();
        tenantProjectDtoMap.values().stream().forEach(p -> {
            TreeNode manageNode = converProjectInfoToNode(p, manageProjectNumMp);
            nodeList.add(manageNode);
        });

        pcaMap.values().stream().forEach(c -> {
            //区域
            TreeNode manageNode = converManageInfoToNode(c, manageProjectNumMp);
            nodeList.add(manageNode);
        });

        return nodeList;
    }

    /**
     * 转换项目信息为树形数据node
     *
     * @param projectDto         项目信息dto
     * @param manageProjectNumMp
     * @return 树形数据node
     */
    private TreeNode converProjectInfoToNode(TenantProjectDto projectDto, Map<String, Integer> manageProjectNumMp) {
        String id = "projectInfo-" + projectDto.getId();
        String pId = "manageInfo-" + projectDto.getManageId();

        TreeNode node = new TreeNode(id, pId, projectDto.getPlatformProjectName(), projectDto.getFillDt().getTime());
        node.setExtra(JSONUtil.parseObj(projectDto));
        node.getExtra().put("dataType", "projectInfo");
        Integer projectNum = manageProjectNumMp.get(pId);
        if (projectNum == null) {
            projectNum = 1;
        } else {
            projectNum++;
        }

        manageProjectNumMp.put(pId, projectNum);
        return node;
    }

    /**
     * 转换区域信息为树形node
     *
     * @param manageInfo         行政区域信息
     * @param manageProjectNumMp
     * @return 树形数据node
     */
    private TreeNode converManageInfoToNode(ManageInfo manageInfo, Map<String, Integer> manageProjectNumMp) {
        String id = "manageInfo-" + manageInfo.getId();
        String pId = "-1";
        Integer level = manageInfo.getLevel();
        if (level.intValue() == 1) {
            pId = "-1";
        } else {
            pId = "manageInfo-" + manageInfo.getParentId();
        }
        TreeNode node = new TreeNode(id, pId, manageInfo.getMangeName(), manageInfo.getSortNo());
        node.setExtra(JSONUtil.parseObj(manageInfo));
        node.getExtra().put("dataType", "manageInfo");

        Integer projectNum = manageProjectNumMp.get(id);
        node.getExtra().put("projectNum", projectNum);

        return node;
    }

    /**
     * 补全安全生产态势数据
     *
     * @param countSafeProductLs 查询出来的安全生产统计数据
     * @param startTime          开始时间
     * @param endTime            结束时间
     * @param checkLevelList     检查等级
     * @return 补全的数据map
     */
    private Map<Integer, TreeMap> compSafeProductionData(List<Map<String, Object>> countSafeProductLs, Date startTime, Date endTime, List<Integer> checkLevelList) {
        //转map
        Map<String, String> mp = new HashMap<>();
        if (CollectionUtil.isNotEmpty(countSafeProductLs)) {
            countSafeProductLs.stream().forEach(c -> {
                StringBuilder keySb = new StringBuilder();
                String val = null;
                Iterator<Map.Entry<String, Object>> it = c.entrySet().iterator();
                while (it.hasNext()) {
                    Map.Entry<String, Object> entry = it.next();
                    String tmpKey = entry.getKey();
                    Object tmpValObj = entry.getValue();
                    if ("checkLevel".equals(tmpKey)) {
                        keySb.append(tmpValObj.toString());
                    }
                    if ("checkMonth".equals(tmpKey)) {
                        keySb.append(tmpValObj.toString()).append(",");
                    }
                    if ("num".equals(tmpKey)) {
                        val = String.valueOf(tmpValObj);
                    }
                }
                if (!StringUtils.isAnyBlank(keySb.toString(), val)) {
                    mp.put(keySb.toString(), val);
                }
            });
        }

        Map<Integer, TreeMap> dataTreeMp = new HashMap<>();
        if (CollectionUtil.isNotEmpty(checkLevelList)) {
            checkLevelList.stream().forEach(c -> {
                TreeMap treeMap = compSafeProductionData(mp, startTime, endTime, c);
                dataTreeMp.put(c, treeMap);
            });
        }

        return dataTreeMp;
    }

    /**
     * 补全安全生产数据
     *
     * @param mp         安全生产数据map
     * @param startTime  开始时间
     * @param endTime    结束时间
     * @param checkLevel 检查等级
     * @return 补全的数据
     */
    private TreeMap compSafeProductionData(Map<String, String> mp, Date startTime, Date endTime, Integer checkLevel) {

        TreeMap treeMap = new TreeMap();

        while (startTime.before(endTime)) {
            String ym = DateUtil.format(startTime, DatePattern.NORM_MONTH_PATTERN);
            String key = ym + "," + checkLevel;
            if (mp.containsKey(key)) {
                String val = mp.get(key);
                treeMap.put(ym, val);
            } else {
                treeMap.put(ym, "0");
            }
            startTime = DateUtil.offsetMonth(startTime, 1);
        }

        return treeMap;
    }

    /**
     * 补全劳务出勤数据
     *
     * @param monthGroupAtList 劳务出勤月统计数据
     * @param startTime        开始时间
     * @param endTime          结束时间
     * @return 补全的劳务出勤数据
     */
    private TreeMap<String,Object> compLaborAttendanceData(List<Map<String, Object>> monthGroupAtList, Date startTime, Date endTime) {
        TreeMap<String,Object> treeMap = new TreeMap();
        //转map
        Map<String, String> mp = new HashMap<>();
        if (CollectionUtil.isNotEmpty(monthGroupAtList)) {
            monthGroupAtList.stream().forEach(c -> {
                String key = null;
                String val = null;
                Iterator<Map.Entry<String, Object>> it = c.entrySet().iterator();
                while (it.hasNext()) {
                    Map.Entry<String, Object> entry = it.next();
                    String tmpKey = entry.getKey();
                    Object tmpValObj = entry.getValue();
                    if ("countTime".equals(tmpKey)) {
                        key = tmpValObj.toString();
                    }
                    if ("num".equals(tmpKey)) {
                        val = String.valueOf(tmpValObj);
                    }
                }
                if (!StringUtils.isAnyBlank(key, val)) {
                    mp.put(key, val);
                }
            });
        }
        while (startTime.before(endTime)) {
            String key = DateUtil.format(startTime, DatePattern.NORM_MONTH_PATTERN);
            if (mp.containsKey(key)) {
                String val = mp.get(key);
                treeMap.put(key, val);
            } else {
                treeMap.put(key, "0");
            }
            startTime = DateUtil.offsetMonth(startTime, 1);
        }
        return treeMap;
    }


}
